const _ = require('lodash')

const Bottleneck = require('bottleneck')

const Response = require('../response')


module.exports = (configuration, {logger}) => {
    logger = logger.child({label: 'downloader'})

    const cache = require('./cache')(configuration)
    const uagetter = require('./uagetter')(configuration)
    const limiter = new Bottleneck({
        maxConcurrent: configuration.httpConcurrent,
        minTime: configuration.httpDelay,
    })

    const retryable = require('./retryable')(require('axios').create())

    const axios = limiter.wrap(retryable)

    return (request, callback, errback) => {
        const resp = cache.get(request)
        if (!_.isUndefined(resp)) {
            logger.info(`${request.url} ${resp.statusCode} {${_.get(resp.body, 'length', 0)} bytes} [cached]`)
            callback(resp)
            return
        }

        const referer = _.defaultTo(request.referer, configuration.referer)
        const useragent = uagetter(request)
        axios({
            url: request.url,
            method: request.method,
            retryable: {
                retry: _.defaultTo(request.httpRetry, configuration.httpRetry),
                onRetry: (attempts, err) => {
                    logger.warn(`${request.url} ${_.get(err, 'response.status', '')} ${_.get(err, 'code', '')} (Referer: ${referer}) ${err}. retry ${attempts} times`)
                }
            },
            timeout: _.defaultTo(request.httpTimeout, configuration.httpTimeout),
            headers: {
                'user-agent': useragent,
                referer: referer,
                accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*',
            }
        }).then(httpResp => {
            const resp = new Response(request, {
                statusCode: httpResp.status,
                body: httpResp.data,
                headers: httpResp.headers,
            })

            cache.set(request, resp)
            logger.info(`${request.url} ${resp.statusCode} {${_.get(resp.body, 'length', 0)} bytes} (Referer: ${referer})`)
            callback(resp)
        }).catch(err => {
            logger.error(`${request.url} ${_.get(err, 'response.status', '')} ${_.get(err, 'code', '')} (Referer: ${referer}) ${err}`)
            errback(err)
        })
    }
}